// 定义一个 构造函数
// 需要两个参数 标签对象 和 数据信息
class setBanner {
    constructor(element, array) {
        // 存储 参数
        this.ele = element;
        this.arr = array;

        // 获取 ul ol 标签对象
        this.ul = this.ele.querySelector('ul');
        this.ol = this.ele.querySelector('ol');

        // 定义 实例化对象的属性和属性值
        this.ullis;
        this.ollis;
        this.length = this.arr.length;
        this.liWidth;
        this.index = 1;
        this.intVal;
        this.bool = true;
    }

    // 定义函数方法

    // 定义一个函数 这个函数专门调用执行 所有需要独立执行的函数程序
    // 这样的函数称为 入口函数 
    // 一般定义名称为 init
    init(){
        this.setPage();
        this.autoLoop();
        this.setMouse();
        this.setClick();
        this.hid();
    }


    // 1 动态渲染生成页面
    setPage() {
        // 定义 变量 存储 生成的字符串
        let ulStr = '';
        let olStr = '';

        // 根据 数组 动态生成字符串标签对象
        // 没有使用this, 使用匿名函数 或者 箭头函数 都可以
        this.arr.forEach(function (item, key) {
            // 生成 ul>li
            ulStr += `<li><img src="../images/${item.name}"></li>`;

            // 生成 ol>li
            olStr += key === 0 ? `<li class="active" name="focus" num="${key}"></li>` : `<li name="focus" num="${key}"></li>`;
        })

        // 将 生成内容 写入 标签
        this.ul.innerHTML = ulStr;
        this.ol.innerHTML = olStr;

        // 获取 所有的 ul>li  ol>li
        this.ullis = this.ele.querySelectorAll('ul>li');
        this.ollis = this.ele.querySelectorAll('ol>li');

        // 克隆 原始ul 中 第一个和最后一个li标签
        const first = this.ullis[0].cloneNode(true);
        const last = this.ullis[this.length - 1].cloneNode(true);

        // 克隆的第一个写入ul最后 克隆的最后有一个写入ul起始
        this.ul.appendChild(first);
        this.ul.insertBefore(last, this.ullis[0]);

        // 重新设定ul宽度
        // 是 当前 li个数 * li宽度
        this.liWidth = this.ullis[0].offsetWidth;

        this.ul.style.width = this.liWidth * (this.length + 2) + 'px';

        // ul 向左 定位 一个li 宽度
        this.ul.style.left = -this.liWidth + 'px';
    }

    // 2 自动轮播
    autoLoop() {
        // 定义定时器 每间隔时间 执行程序
        // 使用this 必须要 修改为箭头函数
        this.intVal = setInterval(() => {
            // 变量累加
            this.index++;

            // 调用函数 设定 焦点样式
            this.setFocusStyle();

            // 新定义一个函数 这个 函数 就是 原始的moveEndFun()
            // 只是改变 this指向 
            // const abc = this.moveEndFun.bind( this );

            // 调用move运动函数 执行ul定位运动改变
            // 参数3 本质上 需要执行程序 的 内存地址
            // 之前 赋值的 函数名称 其中 存储的也是 内存地址
            // 当前 需要 的 还是 这个函数程序 但是 this指向 必须要设定为 实例化对象

            // 因此 需要 定义一个新的函数 和 原始 moveEndFun函数 程序代码相同
            // this指向 修改为 当前的this指向也就是 实例化对象
            // 生成一个新的函数 同时修改this指向 使用的是 bind语法
            // 函数.bind( 新的this , 原始函数的实参 )
            // 当前 原始函数 moveEndFun函数 没有需要的实参 不需要绑定
            // 只需要 修改this指向
            // moveEndFun.bind(this) 执行结果返回值 
            // 就是 新生成 的 函数的 内存地址
            // 可以使用变量储存 也可以 直接赋值 给 move() 运动函数的 参数3

            move(this.ul, { left: -this.liWidth * this.index }, this.moveEndFun.bind(this));

        }, 3000)
    }

    // 3 轮播动画终止回调函数
    //  当前函数执行的方式是 回调 函数 
    //  是在 move运动函数中 调用的
    //  move() 函数 是 声明式 函数 this指向是 window
    moveEndFun() {
        // 回调函数 一般 this 指向都会改变
        // 当前是 变为 undefined
        // 必须要 改变this为 实例化对象
        // console.log( this );

        // 判断 this.index 数值

        // 是最后一个 赋值 1 
        if (this.index === this.length + 2 - 1) {
            // 赋值 1 
            this.index = 1;
            // 重新定位 ul标签对象
            this.ul.style.left = -this.liWidth * this.index + 'px';

            // 是 0 赋值 倒数第二个索引下标
        } else if (this.index === 0) {
            // 赋值 倒数第二个
            this.index = this.length + 2 - 2;
            // 重新定位 ul标签对象
            this.ul.style.left = -this.liWidth * this.index + 'px';
        }

        // 给变量赋值 true
        this.bool = true;
    }

    // 4 焦点样式设定
    setFocusStyle() {
        // 清除所有ol>li样式
        this.ollis.forEach((item, key) => {
            item.classList.remove('active');

            if (key === this.index - 1) {
                item.classList.add('active');
            }
        })

        if (this.index === this.length + 2 - 1) {
            this.ollis[0].classList.add('active');
        } else if (this.index === 0) {
            this.ollis[this.length - 1].classList.add('active');
        }

    }

    // 5 鼠标的移入移出
    setMouse() {
        this.ele.addEventListener('mouseenter', () => {
            clearInterval(this.intVal);
        })

        this.ele.addEventListener('mouseleave', () => {
            this.autoLoop();
        })
    }

    // 6 点击效果
    setClick() {
        // 给父级添加点击事件
        this.ele.addEventListener('click', e => {
            // 判断点击的是 左切换
            if (e.target.getAttribute('name') === 'left') {
                // 防止点击过快
                if (this.bool) {
                    this.bool = false;
                } else {
                    return;
                }

                // this.index 累减 1
                this.index--;

                // 调用函数 设定 焦点样式
                this.setFocusStyle();

                // 使用 move运动函数 运动完成切换
                move(this.ul, { left: -this.liWidth * this.index }, this.moveEndFun.bind(this));

                //  判断点击的是 右切换
            } else if (e.target.getAttribute('name') === 'right') {
                // 防止点击过快
                if (this.bool) {
                    this.bool = false;
                } else {
                    return;
                }



                // this.index 累加 1
                this.index++;

                // 调用函数 设定 焦点样式
                this.setFocusStyle();

                // 使用 move运动函数 运动完成切换
                move(this.ul, { left: -this.liWidth * this.index }, this.moveEndFun.bind(this));

                // 点击的是焦点按钮
            } else if (e.target.getAttribute('name') === 'focus') {
                // 防止点击过快
                if (this.bool) {
                    this.bool = false;
                } else {
                    return;
                }

                // this.index 赋值点击标签存储num值 +1
                this.index = e.target.getAttribute('num') - 0 + 1;

                // 调用函数 设定 焦点样式
                this.setFocusStyle();

                // 使用 move运动函数 运动完成切换
                move(this.ul, { left: -this.liWidth * this.index }, this.moveEndFun.bind(this));

            }
        })
    }

    // 7 浏览器最小化 
    hid() {
        // 给 整个 文档 添加事件 
        // visibilitychange 浏览器显示状态改变事件
        // 浏览器 显示状态 改变 就会触发 
        document.addEventListener('visibilitychange',  () => {
            // document.visibilityState 浏览器显示状态描述
            // hidden 表示 隐藏 
            if (document.visibilityState === 'hidden') {
                // 如果 浏览器是 隐藏/最小化 状态
                // 停止自动轮播
                clearInterval( this.intVal);

                // document.visibilityState 浏览器显示状态描述
                // visible 表示 显示 
            } else if (document.visibilityState === 'visible') {
                // 如果 浏览器是 显示 状态
                // 再次调用 自动轮播
                this.autoLoop();
            }
        })
                window.addEventListener( 'scroll' , function(){

                        if ( $(window).scrollTop() > 1000) {
                            clearInterval( this.intVal);
                        } else if ($(window).scrollTop()<1000) {
                            this.autoLoop();
                        }
                    })
    }

}


/*

回调函数
    在 一个 函数A中 通过 赋值 函数B 的 函数名称/内存地址
    在 函数A中 调用 函数B 这样的操作 称为回调函数

    定义回调函数 是因为 不确定 具体执行程序的代码内容
    需要 外界 输入 执行函数程序的 内存地址
    需要执行程序是 根据 传参 的 函数名称/内存地址
    调用 其中存储的 内存地址 找到对应的存储空间 调用其中的函数程序

    面向对象 中 回调函数 的调用 this指向 一般都不是 实例化对象
    不能使用 修改箭头函数 或者提前定义变量的方式 来修改this指向

    只能是 使用 bind 语法 生成一个新的函数程序 同时 修改 this指向


    const 变量 = 函数.bind(新的this是实例化对象);
    move( 参数1 , 参数2 , 变量 );


    move( 参数1 , 参数2 , 函数.bind(新的this是实例化对象) );



*/

















/*
    面向对象 轮播图分析
        1,  数据
            需要外部输入的数据
                生成轮播图的标签对象
                生成轮播图的数据信息
            根据程序需求 自行定义的 数据数值

        2,  函数方法

            动态渲染生成页面

            自动轮播
                焦点样式设定
                轮播结束函数

            鼠标的移入移出

            点击效果设定

            浏览器最小化

    再次 强调
        如果 子级标签 不需要 触发 事件 一定要使用 enter 和 leave
        如果 自己标签 需要触发 一定是 和 事件委托 配合 再使用 over 和 out

    总结:
        1,  改变this指向的方法
            (1) 匿名函数 改 箭头函数
            (2) 提前定义变量 存储 原始this指向
            (3) 回调函数 是 使用 bind() 声明定义一个新函数 同时 改变this指向

        2,  入口函数 init()
            是 定义一个函数 调用所有 需要 独立执行的 函数方法
            这个函数的名称 一般都叫 init()
            生成了实例化对象 只要调用 init() 就可以调用所有的需要的函数方法
*/







// // 动态渲染生成页面
// function setPage(){
//     // 定义变量储存字符串
//     let ulStr = '';
//     let olStr = '';

//     // 循环变量数组 生成 字符串内容
//     imgArr.forEach( function(item,key){
//         // ul中标签等内容 
//         ulStr += `<li><img src="./images/${item.name}"></li>`;

//         // ol中标签等内容
//         olStr += key === 0 ? `<li class="active" name="focus" num="${key}"></li>` : `<li  name="focus" num="${key}"></li>`;
//     })

//     // 写入标签中
//     oUl.innerHTML = ulStr;
//     oOl.innerHTML = olStr;

//     // 获取 写入的 ul>li ol>li
//     oUlLis = oBanner.querySelectorAll('ul>li');
//     oOlLis = oBanner.querySelectorAll('ol>li');

//     // 克隆 第一个 和 最后 ul>li
//     let first = oUlLis[0].cloneNode(true);
//     let last = oUlLis[oUlLis.length-1].cloneNode(true);

//     // 克隆的第一个 写入 ul最后
//     oUl.appendChild(first);
//     // 克隆的最后一个 写入 ul起始
//     oUl.insertBefore(last , oUlLis[0]);

//     // 获取 li 标签的宽度
//     liWidth = oUlLis[0].offsetWidth;

//     // 重新设定 ul宽度
//     oUl.style.width = liWidth * (length+2) + 'px';

//     // ul标签向左定位一个li宽度
//     oUl.style.left = -liWidth + 'px';

//     /* 

//     // 节点操作方式
//     imgArr.forEach( function(item,key){
//         // 创建 ul>li 标签对象 
//         const ulLi = document.createElement('li');
//         // 创建 ul>li标签中的 img标签
//         const ulLiImg = document.createElement('img');
//         // 设定 img标签,src属性
//         ulLiImg.setAttribute('src' , `./images/${item.name}`);
//         // img标签 要写入 ul>li标签
//         ulLi.appendChild(ulLiImg)
//         // ul>li 标签 写入 ul中
//         oUl.appendChild( ulLi );

//         // 创建 ol>li 标签对象
//         const olLi = document.createElement('li');
//         // 设定 name 属性 和 num属性
//         olLi.setAttribute( 'name' , 'focus' );
//         olLi.setAttribute( 'num' , key );
//         // 第一个ol>li 添加 class,active
//         if( key === 0 ){
//             olLi.classList.add('active');
//         }
//         // ol>li标签写入ol标签中
//         oOl.appendChild(olLi);
//     })

//     */


// }

// // 自动轮播
// function autoLoop(){
//     // 通过 定时器 实现 间隔一段时间 就切换图片
//     // 定时器的时间 必须大于 move()执行的时间
//     intVal = setInterval( function(){
//         // 给 index 累加1
//         index++;

//         // 调用 设定 焦点样式 函数
//         // 一旦执行 轮播图切换 就 执行 样式的改变
//         setFocusStyle();

//         // 调用 move() 运动函数 让 ul根据新的index数值执行定位
//         move( oUl , { left : -liWidth * index } , moveEndFun );        
//     } , 3000 )

// }

// // 运动终止的回调函数
// function moveEndFun(){
//     // 判断 index数值 
//     // 如果是 最后一个li 切换到 第二个li , 给ul瞬间切换定位
//     // 如果是 第一个li 切换到 倒数第二个li , 给ul瞬间切换定位
//     if( index === length+2-1 ){
//         index = 1;
//         oUl.style.left = -liWidth * index + 'px';
//     }else if( index === 0 ){
//         index = length+2-1-1;
//         oUl.style.left = -liWidth * index + 'px';
//     }

//     // 一个li切换结束 bool赋值true
//     bool = true;
// }

// // 焦点样式切换函数
// function setFocusStyle(){
//     // 循环遍历 所有的 ol>li 清除 class.active
//     oOlLis.forEach( function(item,key){
//         // 清除 样式
//         item.classList.remove('active');

//         // 给 和 index对应的 ol>li 添加样式
//         if( key === index-1 ){
//             item.classList.add('active');
//         }
//     })

//     // 两个特殊情况的设定
//     // index 是 最后一个li的索引下标 给 第一个ol>li 添加样式
//     if( index === length+2-1 ){
//         oOlLis[0].classList.add('active');

//     // index 是 第一个li的索引下标 给 最后一个ol>li 添加样式
//     }else if( index === 0 ){
//         oOlLis[oOlLis.length-1].classList.add('active');
//     }
// }

// // 鼠标的移入移出
// function setMouse(){
//     // 给 父级标签 添加事件 子级不要触发

//     // 鼠标移入
//     oBanner.addEventListener('mouseenter' , function(){
//         // 清除定时器
//         clearInterval( intVal );
//     })

//     // 鼠标移入
//     oBanner.addEventListener('mouseleave' , function(){
//         // 再起启动调用 自动轮播函数
//         autoLoop();
//     })
// }

// // 点击效果
// function setClick(){
//     // 给父级添加点击事件 通过事件委托完成
//     oBanner.addEventListener( 'click' , function(e){

//         // 触发事件的标签e.target,name属性值是left,点击的是 左按钮
//         if( e.target.getAttribute('name') === 'left' ){
//             // 防止点击过快
//             if(bool){
//                 bool = false;
//             }else{
//                 return;
//             }

//             // index 累减1
//             index--;

//             // 调用函数 设定焦点样式
//             setFocusStyle();

//             // 根据新的index,运动切换显示图片
//             move( oUl , { left : -liWidth * index } , moveEndFun ); 

//         // 触发事件的标签e.target,name属性值是right,点击的是 右按钮
//         }else if( e.target.getAttribute('name') === 'right' ){
//             // 防止点击过快
//             if(bool){
//                 bool = false;
//             }else{
//                 return;
//             }

//             // index 累加1
//             index++;

//             // 调用函数 设定焦点样式
//             setFocusStyle();

//             // 根据新的index,运动切换显示图片
//             move( oUl , { left : -liWidth * index } , moveEndFun ); 

//         // 触发事件的标签e.target,name属性值是focus,点击的是 焦点按钮
//         }else if( e.target.getAttribute('name') === 'focus' ){
//             // 防止点击过快
//             if(bool){
//                 bool = false;
//             }else{
//                 return;
//             }

//             // 获取 num属性值 +1 赋值给 index变量
//             index = e.target.getAttribute('num')-0 +1;

//             // 调用函数 设定焦点样式
//             setFocusStyle();

//             // 根据新的index,运动切换显示图片
//             move( oUl , { left : -liWidth * index } , moveEndFun ); 
//         }

//     })
// }

// // 浏览器最小化
// function hid(){
//     // 给 整个 文档 添加事件 
//     // visibilitychange 浏览器显示状态改变事件
//     // 浏览器 显示状态 改变 就会触发 
//     document.addEventListener( 'visibilitychange' , function(){
//         console.dir( document.visibilityState )

//         // document.visibilityState 浏览器显示状态描述
//         // hidden 表示 隐藏 
//         if( document.visibilityState === 'hidden' ){
//             // 如果 浏览器是 隐藏/最小化 状态
//             // 停止自动轮播
//             clearInterval( intVal );

//         // document.visibilityState 浏览器显示状态描述
//         // visible 表示 显示 
//         }else if( document.visibilityState === 'visible' ){
//             // 如果 浏览器是 显示 状态
//             // 再次调用 自动轮播
//             autoLoop();
//         }
//     })
// }


